﻿namespace EFS.RC.ClientPlatform.UI
{
	/// <summary>
	/// Connect
	/// </summary>
	public class Platform : EFS.Common.Async.AsyncRoot
	{
		#region Composition

		// sync access
		private static object syncAccess = new object();

		/// <summary>
		/// Connect control
		/// </summary>
		private EFS.RC.ClientPlatform.UI.Connect controlWindow = null;
		private System.Threading.ManualResetEvent controlWindowActiveEvent = null;

		/// <summary>
		/// Session
		/// </summary>
		private EFS.RC.ClientPlatform.Session session = null;

		/// <summary>
		/// Status
		/// </summary>
		public enum PlatformStatus { Idle, Started, Terminated }

		/// Status
		private PlatformStatus status = PlatformStatus.Idle;

		/// <summary>
		/// Connect statis instance
		/// </summary>
		private static Platform instance = null;

		#endregion

		#region Delegates

		/// <summary>
		/// Shut down event delegate
		/// </summary>
		public delegate void TerminatedPlatform();

		/// <summary>
		/// Terminated platform delegate registrar
		/// </summary>
		public TerminatedPlatform OnTerminatedPlatform = null;

		/// <summary>
		/// Shut down event delegate
		/// </summary>
		public delegate void EstablishedSession(EFS.RC.ClientPlatform.Session session);

		/// <summary>
		/// Established session delegate registrar
		/// </summary>
		public EstablishedSession OnEstablishedSession = null;

		#endregion

		#region Constructor

		/// <summary>
		/// Constructor
		/// </summary>
		private Platform()
		{
		}

		#endregion

		#region Usage

		/// <summary>
		/// Get instance to conenct
		/// </summary>
		public static EFS.RC.ClientPlatform.UI.Platform Instance
		{
			get
			{
				// check
				if (null == instance)
				{
					instance = new EFS.RC.ClientPlatform.UI.Platform();
				}

				// get
				return instance;
			}
		}

		/// <summary>
		/// Get status
		/// </summary>
		public PlatformStatus Status
		{
			get { return status; }
		}

		/// <summary>
		/// Describe this object
		/// </summary>
		/// <returns></returns>
		public override System.String Describe()
		{
			return "EFS.RC.ClientPlatform.UI.Platform";
		}

		/// <summary>
		/// Begin platform startup
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginStartup()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginStartup", this);

			// start-up
			return BeginAsyncProcess2(Startup, OnStartupComplete);
		}

		/// <summary>
		/// Begin sutdown
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginShutdown()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginShutdown", this);

			// shutdown
			return BeginAsyncProcess2(Shutdown, OnShutdownComplete);
		}

		#endregion

		#region Startup

		/// <summary>
		/// Startup
		/// </summary>
		private void Startup()
		{
			// log
			EFS.EventLog.Log.LogInfo("Startup", this);

			// sync access
			lock (syncAccess)
			{
				// check already started
				if (status == PlatformStatus.Started)
				{
					return;
				}

				// started
				status = PlatformStatus.Started;
			}
	
			// show control window
			ShowProgressInControlWindow("Connecting to Contact Center", "Connecting to Contact Center");

			// check lync
			BeginAsyncProcess2(Setup, OnSetupComplete);
		}

		/// <summary>
		/// Startup complete
		/// </summary>
		/// <param name="ap"></param>
		private void OnStartupComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnStartupComplete", this);

			try
			{
				// check
				ap.Throw();
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);

				// major problem, can not continue
				BeginShutdown();
			}
		}

		#endregion

		#region Runtime

		/// <summary>
		/// Unhandled exception
		/// </summary>
		/// <param name="ex"></param>
		protected override void OnException(System.Exception ex)
		{
			// check
			EFS.Common.Verify.Reference(ex, "ex");

			// log
			EFS.EventLog.Log.LogException(ex, this);

			// close
			BeginShutdown();
		}

		/// <summary>
		/// Starts control window if not started
		/// </summary>
		private void ShowControlWindow()
		{
			// lock access
			lock (syncAccess)
			{
				// check already up
				if (null != controlWindow)
				{
					return; 
				}

				// check if window is beying activated now
				if (null != controlWindowActiveEvent)
				{
					return;
				}

				// make activation event
				controlWindowActiveEvent = new System.Threading.ManualResetEvent(false);
			}

			// make worker thread
            BeginAsyncProcess2(ControlWindowSTAProcess, ControlWindowSTAProcessCompleter, Threading.Worker);
					
			// wait event
			controlWindowActiveEvent.WaitOne();
			
			// clean activation event
			lock(syncAccess)
			{
				controlWindowActiveEvent.Close();
				controlWindowActiveEvent = null;
			}
		}

        /// <summary>
        /// Control window process completed
        /// </summary>
        /// <param name="ap"></param>
        private void ControlWindowSTAProcessCompleter(EFS.Common.Async.AsyncProcess ap)
        {
            // log
            EFS.EventLog.Log.LogInfo("ControlWindowSTAProcessCompleter", this);

            try
            {
                // throw
                ap.Throw();
            }
            catch (System.Exception ex)
            {
                // log
                EFS.EventLog.Log.LogException(ex, this);
            }
        }

		/// <summary>
		/// Show control window
		/// </summary>
		private void ControlWindowSTAProcess()
		{
			try
			{
				// hook in update
				System.Windows.Application.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new System.Action(() =>
				{
					// show connect control
					Connect controlWindow = new Connect();
					controlWindow.OnTryAgain += OnTryAgain;
					controlWindow.OnGivedUp += OnGivedUp;
					controlWindow.Loaded += OnControlWindowLoaded;
					controlWindow.Closed += OnControlWindowClosed;
					controlWindow.Owner = System.Windows.Application.Current.MainWindow;
					controlWindow.ShowDialog();
				}));
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);

				// close
				BeginShutdown();
			}
		}

		/// <summary>
		/// Control window activated
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void OnControlWindowLoaded(object sender, System.EventArgs e)
		{
			// lock access
			lock (syncAccess)
			{
				// set our window
				controlWindow = (Connect)sender;

				// remove delegate
				controlWindow.Loaded -= OnControlWindowLoaded;

				// set event
				controlWindowActiveEvent.Set();
			}
		}

		/// <summary>
		/// Control window activated
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void OnControlWindowClosed(object sender, System.EventArgs e)
		{
			// lock access
			lock (syncAccess)
			{
				// remove delegate
				controlWindow.Closed -= OnControlWindowClosed;

				// set our window
				controlWindow = null;
			}
		}

		//Show rpogress
		private void ShowProgressInControlWindow(System.String progressTitle, System.String progressMessage)
		{
			// show
			ShowControlWindow();

			// lock access
			try
			{
				// hook in update
				System.Windows.Application.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new System.Action(() =>
				{
					// set
					controlWindow.ShowProgress(progressTitle, progressMessage);
				}));
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		//Show error	
		void ShowErrorInControlWindow(System.String errorTitle, System.String errorMessage)
		{
			// show
			ShowControlWindow();

			// lock access
			try
			{
				// hook in update
				System.Windows.Application.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new System.Action(() =>
				{
					// set
					controlWindow.ShowError(errorTitle, errorMessage);
				}));
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		//Show retry	
		void ShowRetryInControlWindow(System.String retryTitle, System.String retryMessage)
		{
			// show
			ShowControlWindow();

			// lock access
			try
			{
				// hook in update
				System.Windows.Application.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new System.Action(() =>
				{
					// set
					controlWindow.ShowRetry(retryTitle, retryMessage);
				}));
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		/// <summary>
		/// Control window down
		/// </summary>
		private void CloseControlWindow()
		{
			try
			{
				// hook in update
				System.Windows.Application.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new System.Action(() =>
				{
					// check still up
					if (null != controlWindow)
					{
						// remove connect control
						controlWindow.OnTryAgain -= OnTryAgain;
						controlWindow.OnGivedUp -= OnGivedUp;
						controlWindow.Close();
					}
				}));
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		/// <summary>
		/// Setup
		/// </summary>
		private void Setup()
		{
			// check settings
			if (!EFS.Common.Verify.String(EFS.RC.ClientPlatform.Settings.CcServerOfficeServiceSip) ||
				!EFS.Common.Verify.String(EFS.RC.ClientPlatform.Settings.LynkServerFqdn))
			{
				// show control window
				ShowRetryInControlWindow("Connecting to Contact Center", "Check Client Setting (Tools / Setting)");
			}
			else
			{
				// check platform status
				if (EFS.RC.ClientPlatform.Platform.Instance.Status == ClientPlatform.Platform.PlatformStatus.Started)
				{
					SetupSession();
				}
				else
				{
					// setup platform
					SetupPlatform();
				}
			}
		}

		/// <summary>
		/// Setup complete
		/// </summary>
		/// <param name="ap"></param>
		private void OnSetupComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnStartupComplete", this);

			try
			{
				// check
				ap.Throw();
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);

				// notify shut down
				ShowErrorInControlWindow("Platform eror", "Can not continue, check platform settings");
			}
		}

		/// <summary>
		///  Setup platform
		/// </summary>
		private void SetupPlatform()
		{
			// add delegates
			EFS.RC.ClientPlatform.Platform.Instance.OnEstablished += OnPlatformStarted;
			EFS.RC.ClientPlatform.Platform.Instance.OnTerminated += OnPlatformTerminated;

			// start
			EFS.RC.ClientPlatform.Platform.Instance.BeginStartup();
		}

		/// <summary>
		/// Platform started
		/// </summary>
		private void OnPlatformStarted()
		{
			SetupSession();
		}

		/// <summary>
		///  Setup session
		/// </summary>
		private void SetupSession()
		{
			// check
			lock (syncAccess)
			{
				// alredy in progress
				if (null != session)
				{
					return;
				}
			}

			// new session
			session = new ClientPlatform.Session(EFS.RC.ClientPlatform.Platform.Instance.EndPoint);
			session.OnEstablished += OnSessionStarted;
			session.OnTerminated += OnSessionTerminated;

			// start
			session.BeginStartup();
		}

		/// <summary>
		/// Session started
		/// </summary>
		private void OnSessionStarted()
		{
			try
			{
				// close control
				CloseControlWindow();

				// check delegate
				if (null != OnEstablishedSession)
				{
					// call
					OnEstablishedSession(session);
				}
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);

				// notify shut down
				ShowErrorInControlWindow("Applicatin error", "Can not continue");
			}
		}

		/// <summary>
		/// Platform terminated
		/// </summary>
		private void OnPlatformTerminated()
		{
			// remove delegates
			EFS.RC.ClientPlatform.Platform.Instance.OnEstablished -= OnPlatformStarted;
			EFS.RC.ClientPlatform.Platform.Instance.OnTerminated -= OnPlatformTerminated;

			// check status
			lock (syncAccess)
			{
				if (status == PlatformStatus.Started)
				{
					// shut down
					ShowErrorInControlWindow("Platform error", "Can not continue, check user settings");
				}
			}
		}

		/// <summary>
		/// Session terminated
		/// </summary>
		private void OnSessionTerminated()
		{
			// notify window
			lock (syncAccess)
			{
				// check
				if (null != session)
				{
					// remove delegates
					session.OnEstablished -= OnSessionStarted;
					session.OnTerminated -= OnSessionTerminated;
					session = null;
				}
				else
				{
					// no need 
					return;
				}
			}

			// check if terminated was intended
			if (status != PlatformStatus.Terminated)
			{
				// ask user if will try again
				ShowRetryInControlWindow("Session error", "Click 'Try Again' to try to connect to 'Contact Center'");
			}
		}

		/// <summary>
		/// Try start again
		/// </summary>
		private void OnTryAgain()
		{
			// show control
			ShowProgressInControlWindow("Connecting to Contact Center", "Connecting to Contact Center");

			// check lync
			BeginAsyncProcess2(Setup, OnSetupComplete);
		}

		/// <summary>
		/// No longer 
		/// </summary>
		private void OnGivedUp()
		{
			// show control
			BeginShutdown();
		}

		#endregion

		#region Shutdown

		/// <summary>
		/// Shutdown
		/// </summary>
		private void Shutdown()
		{
			// log
			EFS.EventLog.Log.LogInfo("Shutdown", this);

			// lock local acess
			lock (syncAccess)
			{
				// check already terminated
				if (status != PlatformStatus.Terminated)
				{
					// set terinated
					status = PlatformStatus.Terminated;
				}
				else
				{
					// already terminated
					return;
				}
			}

			// check & stop session
			if (null != session && session.Status != ClientPlatform.Session.SessionStatus.Terminated)
			{
				try
				{
					// stop session
					EFS.Common.Async.AsyncProcess apSession = session.BeginShutdown();
					apSession.WaitComplete(EFS.Common.Async.AsyncProcess.Timeout.Short);
				}
				catch (System.Exception ex)
				{
					// log
					EFS.EventLog.Log.LogException(ex, this);
				}
			}
			
			try
			{
				// stop platform
				EFS.Common.Async.AsyncProcess apPlatform = EFS.RC.ClientPlatform.Platform.Instance.BeginShutdown();
				apPlatform.WaitComplete(EFS.Common.Async.AsyncProcess.Timeout.Short);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}

			// make sure control wnd is closed
			CloseControlWindow();
		}

		/// <summary>
		/// Shut down complete
		/// </summary>
		/// <param name="ap"></param>
		private void OnShutdownComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnShutdownComplete", this);

			try
			{
				// check delegate
				if (null != OnTerminatedPlatform)
				{
					// call
					OnTerminatedPlatform();
				}
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		#endregion // Shutdown
	}
}
